/*
 * NaiveBayes.cpp
 *
 *  Created on: 2015-2-14
 *      Author: cpp
 */

#include "NaiveBayes.h"
#include <Vec.h>
#include <vector>
#include<DiscreteEstimator.h>
#include<NormalEstimator.h>
#include"Classifier.h"
#include<iostream>
#include<fstream>
#include<sstream>
#include<Error.h>
#include<StringUtils.h>
#include<SparseVec.h>
#include<DenseVec.h>
#include<stdlib.h>
using namespace std;
#define DEFAULT_NUM_PRECISION 0.01
void NaiveBayes::buildClassifier(vector<Vec*>& datas, int type,
		int numOfClasses) {
	if (datas.size() == 0 || datas[0]->len() < 2) {
		return;
	}
	this->numOfClasses = numOfClasses;
	this->numOfAttr = datas[0]->len() - 1;
	/**
	 * initial the estimators
	 */
	if (estimators == NULL) {
		estimators = new Estimator**[numOfAttr];
		classEstimator = new DiscreteEstimator(numOfClasses, true);
		/**
		 * the vec 0 is the class label,so increase from index 1
		 */
		for (int i = 1; i < numOfAttr; i++) {
			if (type == NOMINAL) {
				estimators[i - 1] = new Estimator*[numOfClasses];
				for (int j = 0; j < numOfClasses; j++) {
					estimators[i - 1][j] = new DiscreteEstimator(2, true);
				}
			} else if (type == NUMERIC) {
				/*
				 estimators[i - 1] = new Estimator*[numOfClasses];
				 for (int j = 0; j < numOfClasses; j++) {
				 estimators[i - 1][j] = new NormalEstimator(
				 DEFAULT_NUM_PRECISION);
				 }
				 */
			}
		}
	}
	for (int i = 0; i < datas.size(); i++) {
		updateClassifier(*datas[i], type, numOfClasses);
	}
}

void NaiveBayes::updateClassifier(Vec& data, int type, int numOfClasses) {
	double sum = 0.0;
	for (int i = 1; i < data.len(); i++) {
		sum += data.get(i);
	}
	for (int i = 1; i < data.len() - 1; i++) {
		(estimators[i - 1][((int) data.get(0)) - 1])->addValue(0.0,
				data.get(i));
		(estimators[i - 1][((int) data.get(0)) - 1])->addValue(1.0,
				sum - data.get(i));
		/*binary
		 *
		 if(data.get(i)>0){
		 (estimators[i - 1][((int) data.get(0))-1])->addValue(0.0, 1.0);
		 }
		 else{
		 (estimators[i - 1][((int) data.get(0))-1])->addValue(1.0, 1.0);
		 }
		 */
	}
	classEstimator->addValue(data.get(0) - 1, 1.0);
}

double* NaiveBayes::distributionForInstance(Vec& instance, int type,
		int numOfClasses) {
	double* probs = new double[numOfClasses];
	for (int j = 0; j < numOfClasses; j++) {
		probs[j] = classEstimator->getProbability(instance.get(0));
	}
	for (int i = 1; i < instance.len(); i++) {
		for (int j = 0; j < numOfClasses; j++) {
			double tmp = max(1e-75, estimators[i - 1][j]->getProbability(0));
			probs[j] *= tmp;
		}
	}
	//normalize the prob
	double sum = 0;
	for (int j = 0; j < numOfClasses; j++) {
		sum += probs[j];
	}
	if (sum != 0.0) {
		for (int j = 0; j < numOfClasses; j++) {
			probs[j] /= sum;
		}
	}
	return probs;
}

NaiveBayes::~NaiveBayes() {
	if (classEstimator != NULL) {
		delete classEstimator;
	}
	if (estimators != NULL) {
		for (int i = 0; i < this->numOfAttr - 1; i++) {
			for (int j = 0; j < this->numOfClasses; j++) {
				delete estimators[i][j];
				estimators[i][j] = NULL;
			}
			delete[] estimators[i];
			estimators[i] = NULL;
		}
		delete[] estimators;
		estimators = NULL;
	}
}

string NaiveBayes::toString() {
	string result = "";
	if (classEstimator != NULL) {
		for (int i = 0; i < numOfClasses; i++) {
			result += StringUtils::toString(i);
			result += ":";
			result += StringUtils::toString(classEstimator->getProbability(i));
			result += " ";
		}
		result += "\n";
	}
	if (estimators != NULL) {
		for (int j = 0; j < numOfClasses; j++) {
			result += StringUtils::toString(j);
			result += ":";
			double sum = 0.0;
			for (int i = 0; i < numOfAttr - 1; i++) {
				double p = estimators[i][j]->getProbability(0);
				result += StringUtils::toString(p);
				result += " ";
				sum += p;
			}
			result += "\n";
			result += StringUtils::toString(sum);
			result += "\n";
		}

	}
	return result;
}


vector<Vec*>* NaiveBayes::read(string filename,int vecSize,bool isSparse){
	vector<Vec*>* result = new vector<Vec*>();
	ifstream fin(filename.c_str(), std::ios::in);
	if(!fin){
		throw SYSTEM_NOT_SUPPORT;
	}
	string line="";
	while(getline(fin,line)){
		vector<string> strs;
		StringUtils::split(line," ",&strs);
		Vec* feas = NULL;
		if(isSparse){
			feas = new SparseVec(vecSize);
		}
		else{
			feas = new DenseVec(vecSize);
		}
		feas->set(0,atof(strs[0].c_str()));
		for(int i=1;i<strs.size()-1;i++){
			double fea = atof(strs[i].c_str());
			feas->set((int)fea,feas->get((int)fea)+1);
		}
		result->push_back(feas);
	}
	fin.close();
	return result;
}


